/*************************************************************************
	> File Name:    config_file_read_only.cpp
	> Author:       陈猛
	> Mail:         chen-meng@example.com
	> Created Time: 2019年09月12日 星期四 16时05分03秒
 ************************************************************************/

#include "config_setting.h"
#include "events/common_log.h"

#define ETC_MAXLINE             1024

ConfigSetting::ConfigSetting()
    : sect_nr_alloc(0)
    , section_nr(0)
    , sections(nullptr)
{
}

ConfigSetting::~ConfigSetting()
{
    // GEN_Printf(LOG_INFO, "config setting %d destruct.", section_nr);
    for(int i = 0; i < section_nr; ++i)
    {
        Section *psect = sections + i;
        if (psect->name.empty())
            continue;

        for (int j=0; j < psect->key_nr; j++)
        {
            psect->keys [j].clear();
            psect->values [j].clear();
        }

        delete [] psect->keys;
        delete [] psect->values;
        psect->name.clear();
    }
    if (sections)
    {
        delete [] sections;
    }
}

bool ConfigSetting::loadFile(const char *file)
{
    FILE* fp = NULL;

    if (!file || (0 == *file) || !(fp = fopen (file, "r")))
    {
        //GEN_Printf(LOG_ERROR, "Open: %s failed, %s", file, strerror(errno));
        return false;
    }

    section_nr    = 0;
    sect_nr_alloc = 16;

    sections = new Section[sect_nr_alloc];

    while(0 == readSection(fp, sections + section_nr))
    {
        ++section_nr;
        if(section_nr == sect_nr_alloc)
        {
            int s = sect_nr_alloc;
            Section *t;

            sect_nr_alloc += 16;
            t = new Section [sect_nr_alloc];

            memcpy(t, sections, s * sizeof(Section));
            sections = t;
        }
    }

    // GEN_Printf(LOG_INFO, "section_nr: %d", section_nr);
    fclose (fp);
    return true;
}

bool ConfigSetting::saveFile(const char *file)
{
    if (!file || (0 == *file) ) return false;

    char tmp_file[strlen(file) + 10];

    sprintf(tmp_file, "%s.tmp", file);

    if(saveTmpFile(tmp_file))
    {
        rename(tmp_file, file);
        return true;
    }
    return false;
}

bool ConfigSetting::saveTmpFile(const char *file)
{
    FILE* fp = NULL;

    if (!file || (0 == *file) || !(fp = fopen (file, "w")))
    {
        GEN_Printf(LOG_ERROR, "Open: %s failed, %s", file, strerror(errno));
        return false;
    }

    for (int i = 0; i < section_nr; ++i)
    {
        // set section name
        ByteString name = sections[i].name;
        if (!name.empty())
        {
            fprintf(fp, "[%s]\n", sections[i].name.string());
            for(int j = 0; j < sections[i].key_nr; ++j)
            {
                ByteString key   = sections[i].keys[j];
                ByteString value = sections[i].values[j];
                if (!key.empty())
                {
                    fprintf(fp, "%s=%s\n", key.string(), value.empty() ? "" : value.string());
                }
            }
            fprintf(fp, "\n");
        }
    }

    fflush (fp);
    fclose (fp);

    return true;
}

void ConfigSetting::clear()
{
    for(int i = 0; i < section_nr; ++i)
    {
        sections[i].clear();
    }
    section_nr = 0;
}

ByteString ConfigSetting::getValue(const ByteString &sec, const ByteString &key)
{
    for (int i = 0; i < section_nr; ++i)
    {
        Section *psec = sections + i;
        if(sec == psec->name)
        {
            return psec->getValue(key);
        }
    }
    return ByteString();
}

ConfigSetting::Section *ConfigSetting::getSection(const ByteString &sec)
{
    for (int i = 0; i < section_nr; ++i)
    {
        Section *psec = sections + i;
        if(sec == psec->name)
        {
            return psec;
        }
    }

    return newCreateSection(sec);
}

ConfigSetting::Section *ConfigSetting::addSection(const ByteString &sec, bool isOnly)
{
    SF_ASSERT(0);
    UNUSED(sec);
    UNUSED(isOnly);
    return NULL;
}

void ConfigSetting::removeSection(const ByteString &sec)
{
    for (int i = 0; i < section_nr; ++i)
    {
        Section *psec = sections + i;
        if (sec == psec->name)
        {
            psec->distclean();

            for (int j = i; j < section_nr-1; ++j)
            {
                sections[j] = sections[j+1];
            }

            --section_nr;
        }
    }
}

void ConfigSetting::removeKey(const ByteString &sec, const ByteString &key)
{
    for (int i = 0; i < section_nr; ++i)
    {
        Section *psec = sections + i;
        if(sec == psec->name)
        {
           psec->removeKey(key);
        }
    }
}

void ConfigSetting::profile()
{
    for (int i = 0; i < section_nr; ++i)
    {
        // set section name
        ByteString name = sections[i].name;

        if (!name.empty())
        {
            GEN_Printf(LOG_INFO, "[%s]", name.string());

            for(int j = 0; j < sections[i].key_nr; ++j)
            {
                ByteString key   = sections[i].keys[j];
                ByteString value = sections[i].values[j];
                if (!key.empty())
                {
                    GEN_Printf(LOG_INFO, "%s=%s", key.string(), value.empty() ? "" : value.string());
                }
            }
        }
    }
}

ByteString ConfigSetting::setValue(const ByteString &sec, const ByteString &key, const ByteString &val)
{
    int i;
    Section *psec = NULL;

    for (i = 0; i < section_nr; ++i)
    {
        if (sections[i].name == sec)
        {
            psec = sections + i;
            break;
        }
    }
    if (!psec)
    {
        // GEN_Printf(LOG_WARN, "Section: %s is not find !!!", sec.string());
        psec = newCreateSection(sec);
    }

    for (i = 0; i < psec->key_nr; ++i)
    {
        if (psec->keys[i] == key)
        {
            break;
        }
    }

    if (i >= psec->key_nr)
    {
        return psec->newKeyValue(key, val);
    } else
    {
        psec->values[i].clear();
        psec->values[i] = ByteString::allocString(val);
        return psec->values[i];
    }
}

int ConfigSetting::readSection(void *fp, ConfigSetting::Section *psect)
{
    char szBuff[ETC_MAXLINE + 1 + 1];
    char* sect_name;

    psect->name = ByteString();
    psect->key_nr = 0;
    psect->keys = NULL;
    psect->values = NULL;

    while(1)
    {
        int bufflen;
        if (!fgets(szBuff, ETC_MAXLINE, (FILE *)fp))
        {
            if (feof ((FILE *)fp))
            {
                if (!psect->name.empty())
                {
                    break;
                } else
                {
                    return -1;
                }
            } else
            {
                GEN_Printf(LOG_ERROR, "Read error, %s", strerror(errno));
                return -1;
            }
        }

        bufflen = strlen (szBuff);

        int lastpos = bufflen - 1;
        while (lastpos >= 0 && ('\n' == szBuff[lastpos] || '\r' == szBuff[lastpos]))
        {
            szBuff [lastpos] = '\0';
            --lastpos;
        }

        if (psect->name.empty()) /* read section name */
        {
            sect_name = get_section_name (szBuff);
            if (!sect_name)
            {
                continue;
            }

            psect->name = ByteString::allocString(sect_name);
            psect->key_nr = 0;
            psect->key_nr_alloc = 16;
            psect->keys    = new ByteString[16];
            psect->values  = new ByteString[16];
        } else
        {
            int ret;
            char *key, *value;

            ret = get_key_value (szBuff, &key, &value);
            if (ret < 0)
            {
                continue;
            } else if (ret > 0)
            {  /* another section begins */
                fseek ((FILE *)fp, -bufflen, SEEK_CUR);
                break;
            }

            psect->newKeyValue (key, value);
        }
    }
    return 0;
}

ConfigSetting::Section *ConfigSetting::newCreateSection(const ByteString &sec)
{
    Section *psec = NULL;
    if (section_nr == sect_nr_alloc)
    {
        int s = sect_nr_alloc;
        Section *t;

        sect_nr_alloc += 16;
        t = new Section [sect_nr_alloc];

        if(sections)
        {
            memcpy(t, sections, s * sizeof(Section));
        }
        sections = t;
    }
    psec = sections + (section_nr++);
    psec->name = ByteString::allocString(sec);
    psec->key_nr = 0;
    psec->key_nr_alloc = 16;
    psec->keys    = new ByteString[16];
    psec->values  = new ByteString[16];

    return psec;
}

char *ConfigSetting::get_section_name(char *section_line)
{
    char* current;
    char* name;

    if (!section_line || 0 == *section_line)
    {
        return NULL;
    }

    current = section_line;

    while (*current == ' ' ||  *current == '\t') current++;

    if (*current == ';' || *current == '#')
    {
        return NULL;
    }

    if (*current++ == '[')
    {
        while (*current == ' ' ||  *current == '\t') current ++;
    } else
    {
        return NULL;
    }

    name = current;
    while (*current != ']' && *current != '\n' &&
          *current != ';' && *current != '#' && *current != '\0')
    {
          current++;
    }

    *current = '\0';
    while (*current == ' ' || *current == '\t')
    {
        *current = '\0';
        current--;
    }

    return name;
}

int ConfigSetting::get_key_value(char *key_line, char **mykey, char **myvalue)
{
    char* current;
    char* tail;
    char* value;

    if (!key_line || 0 == *key_line)
    {
        return -1;
    }

    current = key_line;

    while (*current == ' ' ||  *current == '\t') current++;

    if (*current == ';' || *current == '#')
    {
        return -1;
    }

    if (*current == '[')
    {
        return 1;
    }

    if (*current == '\n' || *current == '\0')
    {
        return -1;
    }

    tail = current;
    while (*tail != '=' && *tail != '\n' &&
          *tail != ';' && *tail != '#' && *tail != '\0')
    {
          tail++;
    }

    value = tail + 1;
    if (*tail != '=')
    {
        *value = '\0';
    }

    *tail-- = '\0';
    while (*tail == ' ' || *tail == '\t')
    {
        *tail = '\0';
        tail--;
    }

    tail = value;
    while (*tail != '\n' && *tail != '\0') tail++;
    *tail = '\0';

    if (mykey)
    {
        *mykey = current;
    }

    if (myvalue)
    {
        *myvalue = value;
    }

    return 0;
}

ByteString ConfigSetting::Section::getValue(const ByteString &key)
{
    for (int j = 0; j < key_nr; ++j)
    {
        if(key == keys[j])
        {
            return values[j];
        }
    }
    return ByteString();
}

void ConfigSetting::Section::getSwapValue(const ByteString &key, ByteString &value)
{
    for (int j = 0; j < key_nr; ++j)
    {
        if(key == keys[j])
        {
            values[j].swap(value);
            return;
        }
    }
}

ByteString ConfigSetting::Section::setValue(const ByteString &key, const ByteString &val)
{
    int i;

    for (i = 0; i < key_nr; ++i)
    {
        if (keys[i] == key)
        {
            break;
        }
    }

    if (i >= key_nr)
    {
        return this->newKeyValue(key, val);
    } else
    {
        values[i].clear();
        values[i] = ByteString::allocString(val);
        return values[i];
    }
}

ByteString ConfigSetting::Section::setValueJudge(const ByteString &key, const ByteString &val, int &state)
{
    int i;

    for (i = 0; i < key_nr; ++i)
    {
        if (keys[i] == key)
        {
            break;
        }
    }

    if (i >= key_nr)
    {
        state += 1;
        return this->newKeyValue(key, val);
    } else
    {
        if (values[i] == val)
        {
            // state = false;
        } else
        {
            state += 1;
            values[i].clear();
            values[i] = ByteString::allocString(val);
        }
        return values[i];
    }
}

ByteString ConfigSetting::Section::newKeyValue(const ByteString &key, const ByteString &value)
{
    if (key_nr == key_nr_alloc)
    {
        int len = key_nr_alloc;

        key_nr_alloc += 16;

        ByteString *k = new ByteString [key_nr_alloc];
        ByteString *v = new ByteString [key_nr_alloc];
        if (keys && values)
        {
            memcpy(k, keys, sizeof(ByteString) * len);
            memcpy(v, values, sizeof(ByteString) * len);

            delete [] keys;
            delete [] values;
        }

        keys   = k;
        values = v;
    }

    ByteString retVal = ByteString::allocString (value);
    keys [key_nr]   = ByteString::allocString (key);
    values [key_nr] = retVal;
    key_nr ++;

    return retVal;
}

void ConfigSetting::Section::removeKey(const ByteString &key)
{
    for (int i = 0; i < key_nr; ++i)
    {
        if (key == keys[i])
        {
            keys[i].clear();
            values[i].clear();

            for (int j = i; j < key_nr-1; ++j)
            {
                keys[j] = keys[j+1];
                values[j] = values[j+1];
            }

            --key_nr;
            return;
        }
    }
}

void ConfigSetting::Section::clear()
{
    name.clear();

    for (int j = 0; j < key_nr; j++)
    {
        keys [j].clear();
        values [j].clear();
    }
    key_nr = 0;
}

void ConfigSetting::Section::distclean()
{
    name.clear();

    for (int j = 0; j < key_nr; j++)
    {
        keys [j].clear();
        values [j].clear();
    }
    delete [] keys;
    delete [] values;
    key_nr = 0;
    key_nr_alloc = 0;
}

// ************************************************************** //
ConfigSettingKV::ConfigSettingKV()
{
    // m_section.name; not used
    m_section.key_nr = 0;
    m_section.key_nr_alloc = 16;
    m_section.keys    = new ByteString[16];
    m_section.values  = new ByteString[16];
}

ConfigSettingKV::~ConfigSettingKV()
{
    for (int j = 0; j < m_section.key_nr; j++)
    {
        m_section.keys [j].clear();
        m_section.values [j].clear();
    }

    delete [] m_section.keys;
    delete [] m_section.values;
}

bool ConfigSettingKV::loadFile(const char *file)
{
    FILE* fp = NULL;
    char szBuff[ETC_MAXLINE + 1 + 1];

    if (!file || (0 == *file) || !(fp = fopen (file, "r")))
    {
        //GEN_Printf(LOG_ERROR, "Open: %s failed, %s", file, strerror(errno));
        return false;
    }
    while(1)
    {
        int bufflen;
        if (!fgets(szBuff, ETC_MAXLINE, (FILE *)fp))
        {
            break;
        }

        bufflen = strlen (szBuff);

        int lastpos = bufflen - 1;
        while (lastpos >= 0 && ('\n' == szBuff[lastpos] || '\r' == szBuff[lastpos]))
        {
            szBuff [lastpos] = '\0';
            --lastpos;
        }

        int ret;
        char *key, *value;

        ret = ConfigSetting::get_key_value (szBuff, &key, &value);
        if (ret < 0)
        {
            continue;
        } else if (ret > 0)
        {  /* another section begins */
            fclose (fp);
            GEN_Printf(LOG_ERROR, "file format invalid.");
            return false;
        }

        m_section.newKeyValue (key, value);
    }

    // GEN_Printf(LOG_INFO, "section_nr: %d", section_nr);
    fclose (fp);
    return true;
}

bool ConfigSettingKV::saveFile(const char *file)
{
    if (!file || (0 == *file) ) return false;

    char tmp_file[strlen(file) + 10];

    sprintf(tmp_file, "%s.tmp", file);

    if (saveTmpFile(tmp_file))
    {
        rename(tmp_file, file);
        return true;
    }
    return false;
}

bool ConfigSettingKV::saveTmpFile(const char *file)
{
    FILE* fp = NULL;

    if (!file || (0 == *file) || !(fp = fopen (file, "w")))
    {
        GEN_Printf(LOG_ERROR, "Open: %s failed, %s", file, strerror(errno));
        return false;
    }

    for (int j = 0; j < m_section.key_nr; ++j)
    {
        ByteString key   = m_section.keys[j];
        ByteString value = m_section.values[j];
        if (!key.empty())
        {
            fprintf(fp, "%s=%s\n", key.string(), value.empty() ? "" : value.string());
        }
    }

    fflush (fp);
    fclose (fp);

    return true;
}
